using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Rogue
{
    public sealed class Timeline
    {
        // keeps track of time, semi-turn based.
        // The world updates in 1/100ths of a second, but the player performing an action
        // jumps the timeline forward until the action's available (so NPCs instantly take all waiting turns)

        Timeline()
        {
            // Timeline constructor
            m_Timeline = new LinkedList<Entity>();

            InputManager.Instance.OnKeyPressed += new InputManager.OnKeyPressedHandler(Instance_OnKeyPressed);
       
            ConsoleWindow.Instance.RegisterFunction("TIMELINE.DISPLAY", CONSOLE_DrawDebug);
            ConsoleWindow.Instance.RegisterFunction("TIMELINE.DISPLAY.ONLYVISIBLE", CONSOLE_RenderOnlyVisible);
        }

        void Instance_OnKeyPressed(object a_Sender, InputManager.KeyEventArgs a_KeyEventArgs)
        {
#if DEBUG
            if (a_KeyEventArgs.Key == Keys.T && a_KeyEventArgs.Control)
            {
                m_bDisplayTimeline = !m_bDisplayTimeline;
            }
#endif
        }

        #region Singleton Hackery
        public static Timeline Instance
        {
            get { return Nested.TimelineInstance; }
        }

        class Nested
        {
            static Nested()
            {
                // Nothing
            }
            internal static readonly Timeline TimelineInstance = new Timeline();
        }
        #endregion

        private LinkedList<Entity> m_Timeline;

        private GameTime m_LastGameTime;
        public GameTime LastGameTime
        {
            get { return m_LastGameTime; }
            set { m_LastGameTime = value; }
        }

        private bool m_HasHero = false;

        
        private bool m_bDisplayTimeline = false;
        private bool m_bDisplayTLforVisibleEntitiesOnly = false;
        public bool DisplayTimeline
        {
            get { return m_bDisplayTimeline; }
            set { m_bDisplayTimeline = value; }
        }

        private Entity m_CurrentEntity;
        public Entity CurrentEntity
            { get { return m_CurrentEntity; } }

        private int m_nCurrentTime = 0;
        public int CurrentTime
            { get { return m_nCurrentTime; }}
        
        #region Update
        public void Update(GameTime dt)
        {
            DrawTimeline();

            if (m_Timeline.Count == 0)
                return;

            m_CurrentEntity = m_Timeline.First.Value;
            Advance(dt);
        }

        public void AdvanceToPlayer()
        {
            if (m_Timeline.Count == 0)
                return;

            while (m_Timeline.Count > 0 && !m_Timeline.First.Value.IsPlayerControlled && m_HasHero)
            {
                m_nCurrentTime = m_Timeline.First.Value.NextThink;
                m_Timeline.First.Value.Think();
            }
        }

        public void Advance(GameTime dt)
        {
            if (m_Timeline.Count == 0)
                return;

            if (m_Timeline.First.Value.IsPlayerControlled)
            {
                // waiting for the player to take a turn
                m_nCurrentTime = m_Timeline.First.Value.NextThink;
                return;
            }

            // update NPCs in realtime.
            int timeToEndForRealtime = m_nCurrentTime + dt.ElapsedRealTime.Milliseconds / 10;
            while (m_Timeline.Count > 0 && !m_Timeline.First.Value.IsPlayerControlled)
            {
                if (m_Timeline.First.Value.NextThink > timeToEndForRealtime)
                {
                    break;
                }

                m_nCurrentTime = m_Timeline.First.Value.NextThink;
                m_Timeline.First.Value.Think();
            }

            m_nCurrentTime = Math.Max(m_nCurrentTime, timeToEndForRealtime);
        }
        #endregion

           

        #region Reset
        public void Reset()
        {
            m_Timeline.Clear();
            m_nCurrentTime = 0;
        }
        #endregion

        #region Add/Remove Functions
        public void Add(Entity a_Entity)
        {
            Entity ReferenceEntity = null;

            if (m_Timeline.Count > 0)
            {
                foreach (Entity e in m_Timeline)
                {
                    // In the event of a tie we're adding this one to the end of
                    // the group sharing the same NextThink time.
                    if (a_Entity.NextThink < e.NextThink)
                    {
                        ReferenceEntity = e;
                        break;
                    }
                }

                if (ReferenceEntity != null)
                    m_Timeline.AddBefore(m_Timeline.Find(ReferenceEntity), a_Entity);
                else
                    m_Timeline.AddLast(a_Entity);
            }
            else
            {
                m_Timeline.AddFirst(a_Entity);
            }

            if (a_Entity.IsPlayerControlled)
                m_HasHero = true;
        }

        public void Remove(Entity a_Entity)
        {
            if (m_Timeline.Contains(a_Entity))
            {
                m_Timeline.Remove(a_Entity);

                if (a_Entity.IsPlayerControlled)
                    m_HasHero = false;
            }
        }


        #endregion
        #region Rendering
        private void DrawTimeline()
        {
            if (!m_bDisplayTimeline)
                return;

            IEnumerable<Entity> CollectorResults = Entity.Collector.GetAllWithin<Entity>(RogueGame.Instance.Map.GetEntity("HERO").Position, RogueGame.Instance.Map.LightCaster.Radius);
            List<Entity> PotentialEntities = new List<Entity>(CollectorResults);
           
            List<string> TextBuffer = new List<string>();
            List<Color> ColorBuffer = new List<Color>();

            TextBuffer.Add("Timeline:");
            ColorBuffer.Add(Color.Gold);

            TextBuffer.Add("---------------------");
            ColorBuffer.Add(Color.Gray);

            int nCounter = 0;

            foreach (Entity e in m_Timeline)
            {
                if (nCounter >= 20)
                    break;

                LightCaster.RayData ray = RogueGame.Instance.Map.LightCaster.GetRay(e.Position);
                if ((ray == null || !ray.m_IsVisible))
                    {
                        if(m_bDisplayTLforVisibleEntitiesOnly)
                            continue;
                    }                

                // Add the text
                TextBuffer.Add("+" + GetCombatTimestamp(e.NextThink - m_Timeline.First.Value.NextThink) + " : " + e.Name);

                if (e.IsPlayerControlled)
                    ColorBuffer.Add(Color.Yellow);
                else
                    ColorBuffer.Add(Color.LightGray);

                nCounter++;
            }
            Point topLeft = new Point(16, 32);
            if (DetailWindow.HeroWindow.IsActive)
                topLeft.X = 16 + DetailWindow.HeroWindow.Position.X + DetailWindow.HeroWindow.WindowWidth;

            ConsoleWindow.Instance.PutFloatingText(TextBuffer.ToArray(), topLeft, ColorBuffer.ToArray());
        }
        #endregion

        public static string GetTimestamp(int a_Time)
        {
            TimeSpan ts = TimeSpan.FromMilliseconds(a_Time * 10);
            string s = string.Format("{0}:{1}:{2}:{3}:{4}", 
                ts.Days.ToString("00"), 
                ts.Hours.ToString("00"), 
                ts.Minutes.ToString("00"), 
                ts.Seconds.ToString("00"),
                Math.Round((double)ts.Milliseconds / 10, 2).ToString("00")
                );
            return s;
        }

        public static string GetCombatTimestamp(int a_Time)
        {
            TimeSpan ts = TimeSpan.FromMilliseconds(a_Time * 10);
            string s = string.Format("{0}:{1}:{2}", 
                ts.Minutes.ToString("00"), 
                ts.Seconds.ToString("00"),
                Math.Round((double)ts.Milliseconds / 10, 2).ToString("00")
                );
            return s;
        }

        #region Console Commands
        private void CONSOLE_RenderOnlyVisible(string[] a_sArgs)
        {
            bool bOnlyVisible = System.Convert.ToBoolean(a_sArgs[0]);
            m_bDisplayTLforVisibleEntitiesOnly = bOnlyVisible;
        }

        private void CONSOLE_DrawDebug(string[] a_sArgs)
        {
            bool bDrawDebug = System.Convert.ToBoolean(a_sArgs[0]);
            m_bDisplayTimeline = bDrawDebug;

        }
        #endregion
    }
}
